﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using System.Text;
using System.Windows.Forms;
using Clock.Utils;

namespace Clock.UI
{
    public partial class FormMain : Form
    {
        private float _h_angle = 0;
        private float _m_angle = 0;
        private float _s_angle = 0;
        private int _min_Id = 1000;
        private string _skinKey = "Skin";
        private string _exitConfirmKey = "ExitConfirm";
        private string _playSoundKey = "PlaySound";
        private IntPtr _sys_Menu = IntPtr.Zero;
        private IntPtr _sub_Menu = IntPtr.Zero;
        private Skin _skin = null;
        private SysClock _clock = null;
        private Lazy<SoundPlayer> _player = null;

        private Skin Skin
        {
            get => this._skin ?? Skins.Default.Value;
            set
            {
                if (value != null)
                {
                    this._skin = value;
                    this.ClientSize = this._skin.BackImage.Size;
                    this.panel_Main.BackgroundImage = this._skin.BackImage;
                    Reg.Instance.Set(this._skinKey, this._skin.Name);
                }
            }
        }

        public FormMain()
        {
            InitializeComponent();

            this._clock = new SysClock(opts =>
            {
                opts.IntervalMilliseconds = i => 1000;
            });

            this._player = new Lazy<SoundPlayer>(() => new SoundPlayer(typeof(FormMain).Assembly.GetManifestResourceStream(typeof(FormMain), "1.wav")));
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            if (Reg.Instance.LoadForm(out var point))
            {
                this.Location = point;
            }

            this._clock.Tick += this.Clock_Tick;
            this._clock.Enable = true;
        }

        private void Clock_Tick(SysClock.TickArgs e)
        {
            var now = DateTime.Now;

            var new_time = new DateTime(
                now.Year,
                now.Month,
                now.Day,
                now.Hour,
                now.Minute,
                now.Second).AddSeconds((int)Math.Round(now.Millisecond / 1000D));

            this.Set_Angles(new_time);

            var state = WinApi.GetMenuState(this._sys_Menu, this._min_Id + 1, WinApi.MF_BYCOMMAND);

            if (state == WinApi.MF_CHECKED || state == WinApi.MF_CHECKED + 128)
            {
                this._player.Value.Play();
            }

            if (this.IsHandleCreated && !this.IsDisposed)
            {
                this.Invoke(new Action(() =>
                {
                    this.Set_Text(new_time);
                    this.panel_Main.Invalidate();
                }));
            }
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);

            var id_Index = this._min_Id + 2;
            var name = Reg.Instance.Get(this._skinKey) ?? string.Empty;
            var exitConfirm = Reg.Instance.Get(this._exitConfirmKey) ?? string.Empty;
            var playSound = Reg.Instance.Get(this._playSoundKey) ?? string.Empty;
            var kvs = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("", "默认") };
            var allKvs = kvs.Concat(Skins.AllSkinNames.Select(n => new KeyValuePair<string, string>(n, n)));
            var currentCheckIndex = 0;

            this._sys_Menu = WinApi.GetSystemMenu(this.Handle, false);

            WinApi.DeleteMenu(this._sys_Menu, 0, WinApi.MF_BYPOSITION);

            WinApi.DeleteMenu(this._sys_Menu, 1, WinApi.MF_BYPOSITION);

            WinApi.DeleteMenu(this._sys_Menu, 2, WinApi.MF_BYPOSITION);

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_SEPARATOR, 0, string.Empty);

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_STRING, this._min_Id, "退出确认(&E)");

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_SEPARATOR, 0, string.Empty);

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_STRING, this._min_Id + 1, "播放音效(&P)");

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_SEPARATOR, 0, string.Empty);

            this._sub_Menu = WinApi.CreateMenu();

            foreach (var kv in allKvs)
            {
                if (kv.Key == name)
                {
                    currentCheckIndex = id_Index;
                }

                WinApi.AppendMenu(this._sub_Menu, WinApi.MF_STRING, id_Index++, kv.Value);
            }

            WinApi.AppendMenu(this._sys_Menu, WinApi.MF_POPUP, (int)this._sub_Menu, "皮肤(&S)");

            if (exitConfirm == "1")
            {
                WinApi.SendMessage(this.Handle, WinApi.WM_SYSCOMMAND, this._min_Id, IntPtr.Zero);
            }

            if (playSound == "1")
            {
                WinApi.SendMessage(this.Handle, WinApi.WM_SYSCOMMAND, this._min_Id + 1, IntPtr.Zero);
            }

            WinApi.SendMessage(this.Handle, WinApi.WM_SYSCOMMAND, Math.Max(currentCheckIndex, this._min_Id + 2), IntPtr.Zero);
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == WinApi.WM_SYSCOMMAND)
            {
                var param = (int)m.WParam;
                var min = this._min_Id + 2;
                var max = min + Skins.AllSkinNames.Count;

                if (param == this._min_Id)
                {
                    var state = WinApi.GetMenuState(this._sys_Menu, param, WinApi.MF_BYCOMMAND);

                    WinApi.CheckMenuItem(this._sys_Menu, param, state == WinApi.MF_CHECKED ? WinApi.MF_UNCHECKED : WinApi.MF_CHECKED);

                    Reg.Instance.Set(this._exitConfirmKey, state == WinApi.MF_CHECKED ? "0" : "1");
                }
                else if (param == this._min_Id + 1)
                {
                    var state = WinApi.GetMenuState(this._sys_Menu, param, WinApi.MF_BYCOMMAND);

                    WinApi.CheckMenuItem(this._sys_Menu, param, state == WinApi.MF_CHECKED ? WinApi.MF_UNCHECKED : WinApi.MF_CHECKED);

                    Reg.Instance.Set(this._playSoundKey, state == WinApi.MF_CHECKED ? "0" : "1");
                }
                else if (param >= min && param <= max)
                {
                    if (param == min)
                    {
                        this.Skin = Skins.Default.Value;
                    }
                    else
                    {
                        var sb = new StringBuilder();

                        WinApi.GetMenuString(this._sub_Menu, param, sb, 100, WinApi.MF_BYCOMMAND);

                        if (sb.Length > 0)
                        {
                            this.Skin = Skins.GetSkinByName(sb.ToString()) ?? Skins.Default.Value;
                        }
                    }

                    WinApi.CheckMenuRadioItem(this._sub_Menu, min, max, param, WinApi.MF_BYCOMMAND);
                }
            }
        }

        private void panel_Main_Paint(object sender, PaintEventArgs e)
        {
            var g = e.Graphics;

            g.TranslateTransform(this.Skin.CenterPoint.X, this.Skin.CenterPoint.Y);

            g.RotateTransform(this._h_angle);
            g.DrawImage(this.Skin.H.Image, this.Skin.H.Center);
            g.RotateTransform(-this._h_angle);

            g.RotateTransform(this._m_angle);
            g.DrawImage(this.Skin.M.Image, this.Skin.M.Center);
            g.RotateTransform(-this._m_angle);

            g.RotateTransform(this._s_angle);
            g.DrawImage(this.Skin.S.Image, this.Skin.S.Center);
            g.RotateTransform(-this._s_angle);
        }

        private void Set_Text(DateTime time)
        {
            this.Text = $"时钟【{time.ToString("yyyy-MM-dd HH:mm:ss")}】";
        }

        private void Set_Angles(DateTime time)
        {
            var h = (float)time.Hour;
            var m = (float)time.Minute;
            var s = (float)time.Second;

            this._s_angle = s / 60F * 360F + this.Skin.S.Angle;
            this._m_angle = ((m + s / 60F) / 60F) * 360F + this.Skin.M.Angle;
            this._h_angle = ((h + m / 60F + s / 60F / 60F) / 12F) * 360F + this.Skin.H.Angle;
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            this._clock.Dispose();

            if (this._player.IsValueCreated)
            {
                this._player.Value.Dispose();
            }

            if (this.WindowState != FormWindowState.Maximized && this.WindowState != FormWindowState.Minimized)
            {
                Reg.Instance.RegForm(this.Location);
            }
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            var state = WinApi.GetMenuState(this._sys_Menu, this._min_Id, WinApi.MF_BYCOMMAND);

            if (state == WinApi.MF_CHECKED)
            {
                if (MessageBox.Show(this, "确定要退出吗？", "信息", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }
    }
}